home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1998 November / Freeware November 1998.img / dist / fw_emacs.idb / usr / freeware / share / emacs / 19.34 / lisp / cmacexp.el.z / cmacexp.el
Lisp/Scheme  |  1998-10-27  |  14KB  |  372 lines

  1. ;;; cmacexp.el --- expand C macros in a region
  2.  
  3. ;; Copyright (C) 1992, 1994, 1996 Free Software Foundation, Inc.
  4.  
  5. ;; Author: Francesco Potorti` <pot@cnuce.cnr.it>
  6. ;; Version: $Id: cmacexp.el,v 1.26 1996/06/07 22:59:27 rms Exp $
  7. ;; Adapted-By: ESR
  8. ;; Keywords: c
  9.  
  10. ;; This file is part of GNU Emacs.
  11.  
  12. ;; GNU Emacs is free software; you can redistribute it and/or modify
  13. ;; it under the terms of the GNU General Public License as published by
  14. ;; the Free Software Foundation; either version 2, or (at your option)
  15. ;; any later version.
  16.  
  17. ;; GNU Emacs is distributed in the hope that it will be useful,
  18. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20. ;; GNU General Public License for more details.
  21.  
  22. ;; You should have received a copy of the GNU General Public License
  23. ;; along with GNU Emacs; see the file COPYING.  If not, write to the
  24. ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  25. ;; Boston, MA 02111-1307, USA.
  26.  
  27. ;; USAGE =============================================================
  28.  
  29. ;; In C mode C-C C-e is bound to c-macro-expand.  The result of the
  30. ;; expansion is put in a separate buffer.  A user option allows the
  31. ;; window displaying the buffer to be optimally sized.
  32. ;;
  33. ;; When called with a C-u prefix, c-macro-expand replaces the selected
  34. ;; region with the expansion.  Both the preprocessor name and the
  35. ;; initial flag can be set by the user.  If c-macro-prompt-flag is set
  36. ;; to a non-nil value the user is offered to change the options to the
  37. ;; preprocessor each time c-macro-expand is invoked.  Preprocessor
  38. ;; arguments default to the last ones entered.  If c-macro-prompt-flag
  39. ;; is nil, one must use M-x set-variable to set a different value for
  40. ;; c-macro-cppflags.
  41.  
  42. ;; A c-macro-expansion function is provided for non-interactive use.
  43.  
  44. ;; INSTALLATION ======================================================
  45.  
  46. ;; Put the following in your ~/.emacs file.
  47.  
  48. ;; If you want the *Macroexpansion* window to be not higher than
  49. ;; necessary: 
  50. ;;(setq c-macro-shrink-window-flag t)
  51. ;;
  52. ;; If you use a preprocessor other than /lib/cpp (be careful to set a
  53. ;; -C option or equivalent in order to make the preprocessor not to
  54. ;; strip the comments):
  55. ;;(setq c-macro-preprocessor "gpp -C")
  56. ;;
  57. ;; If you often use a particular set of flags:
  58. ;;(setq c-macro-cppflags "-I /usr/include/local -DDEBUG"
  59. ;;
  60. ;; If you want the "Preprocessor arguments: " prompt:
  61. ;;(setq c-macro-prompt-flag t)
  62.  
  63. ;; BUG REPORTS =======================================================
  64.  
  65. ;; Please report bugs, suggestions, complaints and so on to
  66. ;; pot@cnuce.cnr.it (Francesco Potorti`).
  67.  
  68. ;; IMPROVEMENTS OVER emacs 18.xx cmacexp.el ==========================
  69.  
  70. ;; - A lot of user and programmer visible changes.  See above.
  71. ;; - #line directives are inserted, so __LINE__ and __FILE__ are
  72. ;;   correctly expanded.  Works even with START inside a string, a
  73. ;;   comment or a region #ifdef'd away by cpp. cpp is invoked with -C,
  74. ;;   making comments visible in the expansion.
  75. ;; - All work is done in core memory, no need for temporary files.
  76.  
  77. ;; ACKNOWLEDGEMENTS ==================================================
  78.  
  79. ;; A lot of thanks to Don Maszle who did a great work of testing, bug
  80. ;; reporting and suggestion of new features.  This work has been
  81. ;; partially inspired by Don Maszle and Jonathan Segal's.
  82.  
  83. ;; BUGS ==============================================================
  84.  
  85. ;; If the start point of the region is inside a macro definition the
  86. ;; macro expansion is often inaccurate.
  87.  
  88.  
  89. (require 'cc-mode)
  90.  
  91. (provide 'cmacexp)
  92.  
  93. (defvar c-macro-shrink-window-flag nil
  94.   "*Non-nil means shrink the *Macroexpansion* window to fit its contents.")
  95.  
  96. (defvar c-macro-prompt-flag nil
  97.   "*Non-nil makes `c-macro-expand' prompt for preprocessor arguments.")
  98.  
  99. (defvar c-macro-preprocessor
  100.   ;; Cannot rely on standard directory on MS-DOS to find CPP.
  101.   (cond ((eq system-type 'ms-dos) "cpp -C")
  102.     ;; Solaris has it in an unusual place.
  103.     ((and (string-match "^[^-]*-[^-]*-\\(solaris\\|sunos5\\)"
  104.                 system-configuration)
  105.           (file-exists-p "/opt/SUNWspro/SC3.0.1/bin/acomp"))
  106.      "/opt/SUNWspro/SC3.0.1/bin/acomp -C -E")
  107.     (t "/lib/cpp -C"))
  108.   "The preprocessor used by the cmacexp package.
  109.  
  110. If you change this, be sure to preserve the `-C' (don't strip comments)
  111. option, or to set an equivalent one.")
  112.  
  113. (defvar c-macro-cppflags ""
  114.   "*Preprocessor flags used by `c-macro-expand'.")
  115.  
  116. (defconst c-macro-buffer-name "*Macroexpansion*")
  117.  
  118. (defun c-macro-expand (start end subst)
  119.   "Expand C macros in the region, using the C preprocessor.
  120. Normally display output in temp buffer, but
  121. prefix arg means replace the region with it.
  122.  
  123. `c-macro-preprocessor' specifies the preprocessor to use.
  124. Prompt for arguments to the preprocessor \(e.g. `-DDEBUG -I ./include')
  125. if the user option `c-macro-prompt-flag' is non-nil.
  126.  
  127. Noninteractive args are START, END, SUBST.
  128. For use inside Lisp programs, see also `c-macro-expansion'."
  129.  
  130.   (interactive "r\nP")
  131.   (let ((inbuf (current-buffer))
  132.     (displaybuf (if subst
  133.             (get-buffer c-macro-buffer-name)
  134.               (get-buffer-create c-macro-buffer-name)))
  135.     (expansion ""))
  136.     ;; Build the command string.
  137.     (if c-macro-prompt-flag
  138.     (setq c-macro-cppflags
  139.           (read-string "Preprocessor arguments: "
  140.                c-macro-cppflags)))
  141.     ;; Decide where to display output.
  142.     (if (and subst
  143.          (and buffer-read-only (not inhibit-read-only))
  144.          (not (eq inbuf displaybuf)))
  145.     (progn
  146.       (message
  147.        "Buffer is read only: displaying expansion in alternate window")
  148.       (sit-for 2)
  149.       (setq subst nil)
  150.       (or displaybuf
  151.           (setq displaybuf (get-buffer-create c-macro-buffer-name)))))
  152.     ;; Expand the macro and output it.
  153.     (setq expansion (c-macro-expansion start end
  154.                        (concat c-macro-preprocessor " "
  155.                            c-macro-cppflags) t))
  156.     (if subst
  157.     (let ((exchange (= (point) start)))
  158.       (delete-region start end)
  159.       (insert expansion)
  160.       (if exchange
  161.           (exchange-point-and-mark)))
  162.       (set-buffer displaybuf)
  163.       (setq buffer-read-only nil)
  164.       (buffer-disable-undo displaybuf)
  165.       (erase-buffer)
  166.       (insert expansion)
  167.       (set-buffer-modified-p nil)
  168.       (if (string= "" expansion)
  169.       (message "Null expansion")
  170.     (c-macro-display-buffer))
  171.       (setq buffer-read-only t)
  172.       (setq buffer-auto-save-file-name nil)
  173.       (bury-buffer displaybuf))))
  174.  
  175.  
  176. ;; Display the current buffer in a window which is either just large
  177. ;; enough to contain the entire buffer, or half the size of the
  178. ;; screen, whichever is smaller.  Do not select the new
  179. ;; window.
  180. ;;
  181. ;; Several factors influence window resizing so that the window is
  182. ;; sized optimally if it is created anew, and so that it is messed
  183. ;; with minimally if it has been created by the user.  If the window
  184. ;; chosen for display exists already but contains something else, the
  185. ;; window is not re-sized.  If the window already contains the current
  186. ;; buffer, it is never shrunk, but possibly expanded.  Finally, if the
  187. ;; variable c-macro-shrink-window-flag is nil the window size is *never*
  188. ;; changed.
  189. (defun c-macro-display-buffer ()
  190.   (goto-char (point-min))
  191.   (c-mode)
  192.   (let ((oldwinheight (window-height))
  193.     (alreadythere            ;the window was already there
  194.      (get-buffer-window (current-buffer)))
  195.     (popped nil))            ;the window popped changing the layout 
  196.     (or alreadythere
  197.     (progn
  198.       (display-buffer (current-buffer) t)
  199.       (setq popped (/= oldwinheight (window-height)))))
  200.     (if (and c-macro-shrink-window-flag    ;user wants fancy shrinking :\)
  201.          (or alreadythere popped))
  202.     ;; Enlarge up to half screen, or shrink properly.
  203.     (let ((oldwin (selected-window))
  204.           (minheight 0)
  205.           (maxheight 0))
  206.       (save-excursion
  207.         (select-window (get-buffer-window (current-buffer)))
  208.         (setq minheight (if alreadythere
  209.                 (window-height)
  210.                   window-min-height))
  211.         (setq maxheight (/ (frame-height) 2))
  212.         (enlarge-window (- (min maxheight
  213.                     (max minheight
  214.                      (+ 2 (vertical-motion (point-max)))))
  215.                    (window-height)))
  216.         (goto-char (point-min))
  217.         (select-window oldwin))))))
  218.  
  219.  
  220. (defun c-macro-expansion (start end cppcommand &optional display)
  221.   "Run a preprocessor on region and return the output as a string.
  222. Expand the region between START and END in the current buffer using
  223. the shell command CPPCOMMAND (e.g. \"/lib/cpp -C -DDEBUG\").
  224. Be sure to use a -C (don't strip comments) or equivalent option.
  225. Optional arg DISPLAY non-nil means show messages in the echo area."
  226.  
  227. ;; Copy the current buffer's contents to a temporary hidden buffer.
  228. ;; Delete from END to end of buffer.  Insert a preprocessor #line
  229. ;; directive at START and after each #endif following START that are
  230. ;; not inside a comment or a string.  Put all the strings thus
  231. ;; inserted (without the "line" substring) in a list named linelist.
  232. ;; If START is inside a comment, prepend "*/" and append "/*" to the
  233. ;; #line directive.  If inside a string, prepend and append "\"".
  234. ;; Preprocess the buffer contents, then look for all the lines stored
  235. ;; in linelist starting from end of buffer.  The last line so found is
  236. ;; where START was, so return the substring from point to end of
  237. ;; buffer. 
  238.   (let ((inbuf (current-buffer))
  239.     (outbuf (get-buffer-create " *C Macro Expansion*"))
  240.     (filename (if (and buffer-file-name
  241.                (string-match (regexp-quote default-directory)
  242.                      buffer-file-name))
  243.               (substring buffer-file-name (match-end 0))
  244.             (buffer-name)))
  245.     (mymsg (format "Invoking %s%s%s on region..."
  246.                c-macro-preprocessor
  247.                (if (string= "" c-macro-cppflags) "" " ")
  248.                c-macro-cppflags))
  249.     (uniquestring "??? !!! ??? start of c-macro expansion ??? !!! ???")
  250.     (startlinenum 0)
  251.     (linenum 0)
  252.     (startstat ())
  253.     (startmarker "")
  254.     (exit-status 0)
  255.     (tempname (make-temp-name (concat
  256.                    (or (getenv "TMPDIR") (getenv "TEMP")
  257.                        (getenv "TMP") "/tmp")
  258.                    "/"))))
  259.     (unwind-protect
  260.     (save-excursion
  261.       (save-restriction
  262.         (widen)
  263.             (let ((in-syntax-table (syntax-table)))
  264.               (set-buffer outbuf)
  265.               (setq buffer-read-only nil)
  266.               (erase-buffer)
  267.               (set-syntax-table in-syntax-table))
  268.         (insert-buffer-substring inbuf 1 end))
  269.  
  270.       ;; We have copied inbuf to outbuf.  Point is at end of
  271.       ;; outbuf.  Inset a newline at the end, so cpp can correctly
  272.       ;; parse a token ending at END.
  273.           (insert "\n")
  274.  
  275.       ;; Save sexp status and line number at START.
  276.       (setq startstat (parse-partial-sexp 1 start))
  277.       (setq startlinenum (+ (count-lines 1 (point))
  278.                 (if (bolp) 1 0)))
  279.  
  280.       ;; Now we insert the #line directives after all #endif or
  281.       ;; #else following START going backward, so the lines we
  282.       ;; insert don't change the line numbers.
  283.       ;(switch-to-buffer outbuf) (debug)    ;debugging instructions
  284.       (goto-char (point-max))
  285.       (while (re-search-backward "\n#\\(endif\\|else\\)\\>" start 'move)
  286.         (if (equal (nthcdr 3 (parse-partial-sexp start (point)
  287.                              nil nil startstat))
  288.                '(nil nil nil 0 nil)) ;neither in string nor in
  289.                          ;comment nor after quote
  290.         (progn
  291.           (goto-char (match-end 0))
  292.           (setq linenum (+ startlinenum
  293.                    (count-lines start (point))))
  294.           (insert (format "\n#line %d \"%s\"\n" linenum filename))
  295.           (goto-char (match-beginning 0)))))
  296.  
  297.       ;; Now we are at START.  Insert the first #line directive.
  298.       ;; This must work even inside a string or comment, or after a
  299.       ;; quote.
  300.       (let* ((startinstring (nth 3 startstat))
  301.          (startincomment (nth 4 startstat))
  302.          (startafterquote (nth 5 startstat))
  303.          (startinbcomment (nth 7 startstat)))
  304.         (insert (if startafterquote " " "")
  305.             (cond (startinstring
  306.                (char-to-string startinstring))
  307.               (startincomment "*/")
  308.               (""))
  309.             (setq startmarker
  310.               (concat "\n" uniquestring
  311.                   (cond (startinstring
  312.                      (char-to-string startinstring))
  313.                     (startincomment "/*")
  314.                     (startinbcomment "//"))
  315.                   (if startafterquote "\\")))
  316.             (format "\n#line %d \"%s\"\n" startlinenum filename)))
  317.  
  318.       ;; Call the preprocessor.
  319.       (if display (message mymsg))
  320.       (setq exit-status
  321.         (call-process-region 1 (point-max)
  322.                      shell-file-name
  323.                      t (list t tempname) nil "-c"
  324.                      cppcommand))
  325.       (if display (message (concat mymsg "done")))
  326.       (if (= (buffer-size) 0)
  327.           ;; Empty output is normal after a fatal error.
  328.           (insert "\nPreprocessor produced no output\n")
  329.         ;; Find and delete the mark of the start of the expansion.
  330.         ;; Look for `# nn "file.c"' lines and delete them.
  331.         (goto-char (point-min))
  332.         (search-forward startmarker)
  333.         (delete-region 1 (point)))
  334.       (while (re-search-forward (concat "^# [0-9]+ \""
  335.                         (regexp-quote filename)
  336.                         "\"") nil t)
  337.         (beginning-of-line)
  338.         (let ((beg (point)))
  339.           (forward-line 1)
  340.           (delete-region beg (point))))
  341.  
  342.       ;; If CPP got errors, show them at the beginning.
  343.       ;; MS-DOS shells don't return the exit code of their children.
  344.       ;; Look at the size of the error message file instead, but
  345.       ;; don't punish those MS-DOS users who have a shell that does
  346.       ;; return an error code.
  347.       (or (and (or (not (boundp 'msdos-shells))
  348.                (not (member (file-name-nondirectory shell-file-name)
  349.                     msdos-shells)))
  350.            (eq exit-status 0))
  351.           (zerop (nth 7 (file-attributes (expand-file-name tempname))))
  352.           (progn
  353.         (goto-char (point-min))
  354.         ;; Put the messages inside a comment, so they won't get in
  355.         ;; the way of font-lock, highlighting etc.
  356.         (insert
  357.          (format "/* Preprocessor terminated with status %s\n\n   Messages from `%s\':\n\n"
  358.              exit-status cppcommand))
  359.         (goto-char (+ (point)
  360.                   (nth 1 (insert-file-contents tempname))))
  361.         (insert "\n\n*/\n")))
  362.       (delete-file tempname)
  363.  
  364.       ;; Compute the return value, keeping in account the space
  365.       ;; inserted at the end of the buffer.
  366.       (buffer-substring 1 (max 1 (- (point-max) 1))))
  367.  
  368.       ;; Cleanup.
  369.       (kill-buffer outbuf))))
  370.  
  371. ;;; cmacexp.el ends here
  372.